#include "stdafx.h"
#include "event/mutex.hpp"
#include "stdlib/exception.hpp"
#include "platform.hpp"
using namespace JHCPP::stdlib::jhException;
#include "stdlib/timestamp.hpp"
using namespace JHCPP::stdlib;

namespace JHCPP
{
	namespace event
	{
		CMutex::CMutex()
		{
		#if defined(JH_OS_WINDOWS)
			::InitializeCriticalSectionAndSpinCount(&m_cs, 4000);
		#elif defined(JH_OS_LINUX)
			pthread_mutexattr_t attr;
			pthread_mutexattr_init(&attr);
			if (pthread_mutex_init(&m_mutex, &attr))
			{
				pthread_mutexattr_destroy(&attr);
				throw SystemException("cannot create mutex");
			}
			pthread_mutexattr_destroy(&attr);
		#endif
		}

		CMutex::~CMutex()
		{
		#if defined(JH_OS_WINDOWS)
			::DeleteCriticalSection(&m_cs);
		#elif defined(JH_OS_LINUX)
			pthread_mutex_destroy(&m_mutex);
		#endif
		}
		
		void CMutex::lock()
		{
		#if defined(JH_OS_WINDOWS)
			try
			{
				::EnterCriticalSection(&m_cs);
			}
			catch (...)
			{
				throw SystemException("cannot lock mutex");
			}
		#elif defined(JH_OS_LINUX)
			if (pthread_mutex_lock(&m_mutex)) 
				throw SystemException("cannot lock mutex");
		#endif
		}

		void CMutex::lock(long milliseconds)
		{
			if (!tryLock(milliseconds))
				throw TimeoutException();
		}

		bool CMutex::tryLock()
		{
		#if defined(JH_OS_WINDOWS)
			try
			{
				return ::TryEnterCriticalSection(&m_cs) != 0;
			}
			catch (...)
			{
				throw SystemException("cannot lock mutex");
			}
		#elif defined(JH_OS_LINUX)
			int rc = pthread_mutex_trylock(&m_mutex);
			if (rc == 0)
				return true;
			else if (rc == EBUSY)
				return false;
			else
				throw SystemException("cannot lock mutex");
		#endif
		}

		bool CMutex::tryLock(long milliseconds)
		{
			const int sleepMillis = 5;
			CTimeStamp now;
			CTimeStamp::TimeDiff diff(CTimeStamp::TimeDiff(milliseconds)*1000);

			do
			{
			#if defined(JH_OS_WINDOWS)
				try
				{
					if (::TryEnterCriticalSection(&m_cs) == TRUE)
						return true;
				}
				catch (...)
				{
					throw SystemException("cannot lock mutex");
				}
				Sleep(sleepMillis);
			#elif defined(JH_OS_LINUX)
				int rc = pthread_mutex_trylock(&m_mutex);
				if (rc == 0)
					return true;
				else if (rc != EBUSY)
					throw SystemException("cannot lock mutex");

				struct timeval tv;
				tv.tv_sec  = 0;
				tv.tv_usec = sleepMillis * 1000;
				select(0, NULL, NULL, NULL, &tv);
			#endif
			}while (!now.isElapsed(diff));
			return false;
		}

		void CMutex::unlock()
		{
		#if defined(JH_OS_WINDOWS)
			try
			{
				::LeaveCriticalSection(&m_cs);
			}
			catch (...)
			{
				throw SystemException("cannot lock mutex");
			}
		#elif defined(JH_OS_LINUX)
			if (pthread_mutex_unlock(&m_mutex))
				throw SystemException("cannot unlock mutex");
		#endif
		}
	}//end of namespace event
}//end of namespace JHCPP
